package resources

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-xorm/xorm"
	"strconv"
	"strings"
	"time"
	"xlib/log"

	D "TCP/define"
	"TCP/protocol/acs"
	"TCP/resources/db"
)

//Dac 门禁
type Dac struct {
	ctx    context.Context
	cancel context.CancelFunc

	dbConn *db.DacConn
}

//NewDac 构造函数
func NewDac() (*Dac, error) {
	d := &Dac{}
	d.ctx, d.cancel = context.WithCancel(context.Background())

	err := d.init()
	if err != nil {
		return nil, err
	}

	return d, nil
}

func (d *Dac) init() (err error) {
	d.dbConn = db.NewDacConn()
	return
}

func (d *Dac) stop() {
	d.cancel()
	return
}

//SaveDrvInfo 保存设备信息数据
func (d *Dac) GetDrvInfo(sn string) (int, error, D.DriverInfo) {
	drvInfo, err := d.dbConn.GetDrvInfo(sn)
	if err != nil {
		return D.EcUnknown, err, D.DriverInfo{}
	}
	driverInfo := D.DriverInfo{
		ID:          drvInfo.ID,
		SN:          drvInfo.SN,
		UpdateTime:  drvInfo.UpdateTime.Format(D.TimeDataFormat),
		DrvTime:     drvInfo.DrvTime.Format(D.TimeDataFormat),
		Desc:        drvInfo.Desc,
		SimStatus:   drvInfo.SimStatus,
		ModType:     drvInfo.ModType,
		ModVER:      drvInfo.ModVER,
		CSQ:         drvInfo.CSQ,
		Rate:        drvInfo.Rate,
		IMEI:        drvInfo.IMEI,
		IMSI:        drvInfo.IMSI,
		ICCID:       drvInfo.ICCID,
		PhoneNum:    drvInfo.PhoneNum,
		BootVer:     drvInfo.BootVer,
		CoreVer:     drvInfo.CoreVer,
		TeVer:       drvInfo.TeVer,
		ModSdkVer:   drvInfo.ModSdkVer,
		ModCoreVer:  drvInfo.ModCoreVer,
		FactoryMode: drvInfo.FactoryMode,
	}
	return D.EcSuccess, nil, driverInfo
}

//GetCSQRecord 获取信号强度历史数据
func (d *Dac) GetCSQRecord(sn string) (int, error, D.CSQData) {
	drvEvents, err := d.dbConn.GetCSQRecord(sn)
	if err != nil {
		return D.EcUnknown, err, D.CSQData{}
	}

	csqData := D.CSQData{
		CSQ:     make([]int, 0),
		CSQTime: make([]string, 0),
	}
	for i := len(drvEvents); i >= 1; i-- {
		csqData.CSQ = append(csqData.CSQ, drvEvents[i-1].EventPara)
		csqData.CSQTime = append(csqData.CSQTime, drvEvents[i-1].EventTime.Format("15:04"))
	}
	return D.EcSuccess, nil, csqData
}

//SaveDrvInfo 保存设备信息数据
func (d *Dac) SaveDrvInfo(info db.DrvInfo) (int, error) {
	err := d.dbConn.SaveDrvInfo(info)
	if err != nil {
		return D.EcUnknown, err
	}
	return D.EcSuccess, nil
}

//SaveDrvEvent 保存设备信息事件
func (d *Dac) SaveDrvEvent(events []db.DrvEvent) (int, error) {
	err := d.dbConn.SaveDrvEvent(events)
	if err != nil {
		return D.EcUnknown, err
	}
	return D.EcSuccess, nil
}

//GetDoors 获取门信息
func (d *Dac) GetDoors() (int, error, []D.DoorInfo) {
	acsDoors, err := d.dbConn.GetDoors()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, nil
	}

	doors := make([]D.DoorInfo, 0, len(acsDoors))
	for _, ad := range acsDoors {
		d := D.DoorInfo{
			DoorNo:   byte(ad.DoorID),
			DoorName: ad.DoorName,
		}
		doors = append(doors, d)
	}

	return D.EcSuccess, nil, doors
}

//GetDoorState 获取门状态
func (d *Dac) GetDoorState() (int, error, map[string]int) {
	acsDoors, err := d.dbConn.GetDoors()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, nil
	}

	parseDoorMap := func(mapStr string) map[int]string {
		statusStrs := strings.Split(mapStr, ";")
		res := make(map[int]string)
		for _, ss := range statusStrs {
			kv := strings.Split(ss, "=")
			if len(kv) == 2 {
				v, err := strconv.Atoi(kv[1])
				if err != nil {
					log.Debug("Parse map failed", err)
					continue
				}
				res[v] = kv[0]
			}
		}
		return res
	}

	doorState := make(map[string]int, 0)
	for _, ad := range acsDoors {
		key := fmt.Sprintf("door%d", ad.DoorID)
		doorMap := parseDoorMap(ad.DoorMap)
		doorState[key] = DoorStatusMap[doorMap[ad.DoorStatus]]
		var status D.DoorStat
		err := json.Unmarshal([]byte(ad.Reserve1), &status)
		if err != nil {
			status.RunStat = 0
		}
		doorState[key+"_status"] = AclRunStat2MDCStat[status.RunStat]
	}

	return D.EcSuccess, nil, doorState
}

//SetDoorState 远程控制
func (d *Dac) SetDoorState(doorState map[string]int, ip string) (int, error) {
	ds, err := d.dbConn.GetDoors()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err
	}

	dm := make(map[int]db.AcsDoor, 0)
	for _, d := range ds {
		dm[d.DoorID] = d
	}
	openMethod := D.RemotePlatformOpenMethod
	if v, ok := doorState["open_method"]; ok {
		openMethod = v
		delete(doorState, "open_method")
	}
	//doorBlock := make(map[int]int, 0)
	//TODO 添加doorN_block + 常闭
	ctrlDoorMap := make(map[int][]D.CtlDoor, 0)
	for door, ctrl := range doorState {
		if strings.Contains(door, "block") {
			continue
		}
		doorID, _ := strconv.Atoi(strings.TrimPrefix(door, "door"))
		d, ok := dm[doorID]
		if !ok {
			return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist + door)
		}

		if _, ok := ctrlDoorMap[ctrl]; !ok {
			ctrlDoorMap[ctrl] = make([]D.CtlDoor, 0)
		}
		ctrlDoorMap[ctrl] = append(ctrlDoorMap[ctrl], D.CtlDoor{ControllerID: d.ControllerID, DoorNo: d.DoorNo})
	}
	log.Debugf("set door state %#v", ctrlDoorMap)

	for ctrl, doors := range ctrlDoorMap {
		req := map[string]interface{}{
			"doors":       doors,
			"ctl":         ctrl,
			"ip":          ip,
			"open_method": openMethod,
		}

		data, err := json.Marshal(req)
		if err != nil {
			log.Errorf("marshal error: %s", err)
			return D.EcUnknown, err
		}

		err = execute(D.DoorControl, string(data))
		if err != nil {
			log.Error(err)
			return D.EcSetCommandFail, err
		}
	}

	return D.EcSuccess, nil
}

// 获取星期数据
func (d *Dac) getWeekInfo(openTime *db.AcsOpenTime, timeInfo *D.TimeInfo) {
	if openTime == nil {
		return
	}

	if openTime.IsMonday == 1 {
		timeInfo.Week = append(timeInfo.Week, 1)
	}
	if openTime.IsTuesday == 1 {
		timeInfo.Week = append(timeInfo.Week, 2)
	}
	if openTime.IsWednesday == 1 {
		timeInfo.Week = append(timeInfo.Week, 3)
	}
	if openTime.IsThursday == 1 {
		timeInfo.Week = append(timeInfo.Week, 4)
	}
	if openTime.IsFriday == 1 {
		timeInfo.Week = append(timeInfo.Week, 5)
	}
	if openTime.IsSaturday == 1 {
		timeInfo.Week = append(timeInfo.Week, 6)
	}
	if openTime.IsSunday == 1 {
		timeInfo.Week = append(timeInfo.Week, 7)
	}
}

//GetTimeGroup 获取时段信息
func (d *Dac) GetTimeGroup(groupNo int) (int, error, D.TimeInfo) {
	if groupNo < 0 || groupNo > 11 {
		return D.EcGroupNoNotExist, errors.New(D.EmGroupNoNotExist + strconv.Itoa(groupNo)), D.TimeInfo{}
	}

	openTimes, err := d.dbConn.GetOpenTime(groupNo)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, D.TimeInfo{}
	}

	timeInfo := &D.TimeInfo{GroupNo: groupNo, Week: make([]int, 0), Timezone: make([]D.TimeSlot, 0, 3)}
	if len(openTimes) > 0 {
		openTime := openTimes[0]
		d.getWeekInfo(openTime, timeInfo)
	}

	for i := 0; i < 3; i++ {
		if len(openTimes) > i {
			timeSlots := make([]D.TimeSlot, 0)
			err := json.Unmarshal([]byte(openTimes[i].TimeSlots), &timeSlots)
			if err != nil {
				log.Error(err)
				return D.EcUnknown, err, *timeInfo
			}

			timeInfo.Timezone = append(timeInfo.Timezone, timeSlots[0])
		} else {
			timeInfo.Timezone = append(timeInfo.Timezone, D.TimeSlot{Begin: "00:00", End: "00:00"})
		}
	}

	return D.EcSuccess, nil, *timeInfo
}

//SetTimeGroup 设置时段信息
func (d *Dac) SetTimeGroup(timeInfo D.TimeInfo) (int, error) {
	if timeInfo.GroupNo < 0 || timeInfo.GroupNo > 11 {
		return D.EcGroupNoNotExist, errors.New(D.EmGroupNoNotExist + strconv.Itoa(timeInfo.GroupNo))
	}

	err := d.dbConn.SetOpenTime(timeInfo)
	if err != nil {
		log.Error(err)
		return D.EcSetCommandFail, err
	}

	go d.Execute()

	return D.EcSuccess, nil
}

//ClearTimeGroup 清空时段信息
func (d *Dac) ClearTimeGroup(groupNo int) (int, error) {
	if groupNo < 0 || groupNo > 11 {
		return D.EcGroupNoNotExist, errors.New(D.EmGroupNoNotExist + strconv.Itoa(groupNo))
	}

	err := d.dbConn.DeleteOpenTime(groupNo)
	if err != nil {
		log.Error(err)
		return D.EcSetCommandFail, err
	}

	go d.Execute()

	return D.EcSuccess, nil
}

//GetCards 获取所有卡信息
func (d *Dac) GetCards() (int, error, []D.CardInfo) {
	acsCards, err := d.dbConn.GetCards("")
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, nil
	}

	cards := make([]D.CardInfo, 0, len(acsCards))
	for _, acsCard := range acsCards {
		card := D.CardInfo{
			CardNo:   acsCard.OriginCardID,
			UserName: acsCard.PersonName,
			Password: acsCard.CardPwd,
			CardFlag: acsCard.CardStatus - 1,
		}

		timePris, err := d.dbConn.GetPrivilegeByCard(acsCard.OriginCardID)
		if err != nil {
			log.Error(err)
			return D.EcUnknown, err, cards
		}

		if len(timePris) == 0 {
			card.GroupNo = -1
			cards = append(cards, card)
			continue
		}
		card.GroupNo = timePris[0].OpenIndex / 3

		card.Door, err = d.dbConn.GetDoorByTimePris(timePris)
		if err != nil {
			log.Error(err)
			return D.EcUnknown, err, cards
		}

		cards = append(cards, card)
	}

	return D.EcSuccess, nil, cards
}

//GetCard 获取卡信息
func (d *Dac) GetCard(cardNo string) (int, error, D.CardInfo) {
	acsCards, err := d.dbConn.GetCards(cardNo)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, D.CardInfo{}
	}

	if len(acsCards) == 0 {
		return D.EcCardNoNotExist, errors.New(D.EmCardNoNotExist + cardNo), D.CardInfo{}
	}

	card := D.CardInfo{
		CardNo:   acsCards[0].OriginCardID,
		UserName: acsCards[0].PersonName,
		Password: acsCards[0].CardPwd,
		CardFlag: acsCards[0].CardStatus - 1,
	}

	timePris, err := d.dbConn.GetPrivilegeByCard(acsCards[0].OriginCardID)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, card
	}

	if len(timePris) == 0 {
		card.GroupNo = -1
		return D.EcSuccess, nil, card
	}
	log.Debugf("timePris: %#v", *timePris[0])

	card.GroupNo = timePris[0].OpenIndex / 3
	card.Door, err = d.dbConn.GetDoorByTimePris(timePris)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, card
	}

	return D.EcSuccess, nil, card
}

//AddCard 新增卡信息
func (d *Dac) AddCard(cardInfo D.CardInfo) (int, error) {
	doors, err := d.dbConn.GetDoorByNos(cardInfo.Door)
	if err != nil {
		log.Error(err)
		return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist)
	}

	if len(doors) != len(cardInfo.Door) {
		log.Errorf("DoorNo not existed", cardInfo.Door)
		return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist)
	}

	err = d.dbConn.AddCard(cardInfo)
	if err != nil {
		log.Error(err)
		return D.EcSetCommandFail, err
	}

	d.Execute()

	return D.EcSuccess, nil
}

//SetCard 设置卡信息
func (d *Dac) SetCard(cardInfo D.CardInfo) (int, error) {
	acsCards, err := d.dbConn.GetCards(cardInfo.CardNo)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err
	}

	if len(acsCards) == 0 {
		return D.EcCardNoNotExist, errors.New(D.EmCardNoNotExist + cardInfo.CardNo)
	}
	doors, err := d.dbConn.GetDoorByNos(cardInfo.Door)

	if err != nil {
		log.Error(err)
		return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist)
	}

	if len(doors) != len(cardInfo.Door) {
		log.Errorf("DoorNo not existed", cardInfo.Door)
		return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist)
	}

	err = d.dbConn.SetCard(cardInfo)
	if err != nil {
		log.Error(err)
		return D.EcSetCommandFail, err
	}

	d.Execute()

	return D.EcSuccess, nil
}

//DeleteCard 删除卡信息
func (d *Dac) DeleteCard(cardNo string) (int, error) {
	acsCards, err := d.dbConn.GetCards(cardNo)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err
	}

	if len(acsCards) == 0 {
		return D.EcCardNoNotExist, errors.New(D.EmCardNoNotExist + cardNo)
	}

	err = d.dbConn.DeleteCard(cardNo)
	if err != nil {
		log.Error(err)
		return D.EcSetCommandFail, err
	}

	d.Execute()

	return D.EcSuccess, nil
}

//GetEvent 获取记录
func (d *Dac) GetEvent(recordIndex, alarmIndex int) (int, error, *D.EventInfo) {
	event := &D.EventInfo{
		RecordLastGet: 0,
		Record:        make([]D.RecordInfo, 0),
		AlarmLastGet:  0,
		Alarm:         make([]D.AlarmInfo, 0),
	}

	err := d.dbConn.CheckEventDel()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	//if recordIndex == 0  {
	latestRecord, err := d.dbConn.GetLatestRecord()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	event.RecordLastGet = int(latestRecord.ID)

	//}

	//if alarmIndex == 0 {
	latestAlarm, err := d.dbConn.GetLatestAlarm()
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	event.AlarmLastGet = int(latestAlarm.ID)
	//}

	records, err := d.dbConn.GetEventByIndex(recordIndex)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	alarms, err := d.dbConn.GetAlarmByIndex(alarmIndex)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	//if len(records) > 0 {
	//	event.RecordLastGet = int(records[len(records)-1].ID)
	//}
	//if len(alarms) > 0 {
	//	event.AlarmLastGet = int(alarms[len(alarms)-1].ID)
	//}

	for _, r := range records {
		door, err := d.dbConn.GetDoorByNo(r.ControllerID, r.DoorNo)
		if err != nil {
			log.Error(err)
			return D.EcUnknown, err, event
		}

		direction := 0
		if strings.Contains(r.Content, "出门") {
			direction = 1
		}

		record := D.RecordInfo{
			Index:     int(r.ID),
			Time:      r.EventTime.Format("2006-01-02 15:04:05"),
			CardNo:    r.CardID,
			UserName:  r.EmployeeName,
			Door:      byte(door.DoorID),
			Direction: direction,
		}
		record.Type, record.Desc = GetRecordAlarmTypeDesc(&r)
		if record.Desc == "" {
			record.Desc = r.Content
		}
		event.Record = append(event.Record, record)
	}

	for _, a := range alarms {
		door, err := d.dbConn.GetDoorByNo(a.ControllerID, a.DoorNo)
		if err != nil {
			log.Error(err)
			return D.EcUnknown, err, event
		}

		alarm := D.AlarmInfo{
			Index: int(a.ID),
			Time:  a.EventTime.Format("2006-01-02 15:04:05"),
			Door:  byte(door.DoorID),
		}
		alarm.Type, alarm.AlmDesc = GetRecordAlarmTypeDesc(&a)
		if alarm.AlmDesc == "" {
			alarm.AlmDesc = a.Content
		}
		alarm.State = GetAlarmState(&a)
		event.Alarm = append(event.Alarm, alarm)
	}

	return D.EcSuccess, nil, event
}

//GetHistoryEvent 获取历史记录
func (d *Dac) GetHistoryEvent(timeBegin, timeEnd string, door []int) (int, error, *D.EventHty) {
	event := &D.EventHty{
		Record: make([]D.RecordInfo, 0),
		Alarm:  make([]D.AlarmInfo, 0),
	}

	records, alarms, err := d.dbConn.GetHistoryEvent(timeBegin, timeEnd, door)
	if err != nil {
		log.Error(err)
		return D.EcUnknown, err, event
	}

	event.RecordCount, event.AlarmCount = len(records), len(alarms)

	for _, r := range records {
		door, err := d.dbConn.GetDoorByNo(r.ControllerID, r.DoorNo)
		if err != nil {
			log.Error(err)
			if err == xorm.ErrNotExist {
				return D.EcDoorIDNotExist, errors.New(D.EmDoorIDNotExist), event
			}
			return D.EcUnknown, err, event
		}

		direction := 0
		if strings.Contains(r.Content, "出门") {
			direction = 1
		}

		record := D.RecordInfo{
			Index:     int(r.ID),
			Time:      r.EventTime.Format("2006-01-02 15:04:05"),
			CardNo:    r.CardID,
			UserName:  r.EmployeeName,
			Door:      byte(door.DoorID),
			Direction: direction,
		}
		record.Type, record.Desc = GetRecordAlarmTypeDesc(&r)
		if record.Desc == "" {
			record.Desc = r.Content
		}
		event.Record = append(event.Record, record)
	}

	for _, a := range alarms {
		door, err := d.dbConn.GetDoorByNo(a.ControllerID, a.DoorNo)
		if err != nil {
			log.Error(err)
			return D.EcUnknown, err, event
		}

		alarm := D.AlarmInfo{
			Index: int(a.ID),
			Time:  a.EventTime.Format("2006-01-02 15:04:05"),
			Door:  byte(door.DoorID),
		}
		alarm.Type, alarm.AlmDesc = GetRecordAlarmTypeDesc(&a)
		alarm.State = GetAlarmState(&a)
		if alarm.AlmDesc == "" {
			alarm.AlmDesc = a.Content
		}
		event.Alarm = append(event.Alarm, alarm)
	}

	return D.EcSuccess, nil, event
}

func (d *Dac) SyncTime() error {
	return execute(D.SyncTime, "")
}

func (d *Dac) Reset() error {
	return execute(D.Reset, "")
}

//Execute 生效
func (d *Dac) Execute() {
	err := execute(D.Execute, "")
	if err != nil {
		log.Errorf("execute error: %s", err)
	}
}

func execute(action, data string) error {
	client := acs.NewAccessInnerClient(D.AccessConn)
	ctx, cc := context.WithTimeout(context.Background(), time.Second*D.Cfg.AccessTimeout)
	defer cc()

	ret, err := client.ExecuteCommand(ctx, &acs.AccessValue{Action: action, Data: data})
	if err != nil {
		return err
	}

	if ret != nil && ret.ErrorCode != int32(D.EcSuccess) {
		return errors.New(ret.ErrorMsg)
	}

	return nil
}
